package com.hontech.desktoppastacooker.conn.usb

import android.hardware.usb.UsbEndpoint
import com.felhr.usbserial.UsbSerialDevice
import com.hontech.desktoppastacooker.conn.Proto
import com.hontech.desktoppastacooker.conn.SerialPort
import com.hontech.desktoppastacooker.utils.log
import com.hontech.desktoppastacooker.utils.toUint8
import java.io.IOException

class UsbSerialPort (private val port: UsbSerialDevice) : SerialPort {

    private val readDataBuf: ByteArray
    private var readDataSize = 0
    private var readDataIndex = 0

    private val maxInSize: Int
    private val maxOutSize: Int

    init {
        val ret = port.syncOpen()
        if (!ret) {
            throw IOException("打开串口失败")
        }
        port.setBaudRate(115200)
        port.setDataBits(UsbSerialDevice.DATA_BITS_8)
        port.setFlowControl(UsbSerialDevice.FLOW_CONTROL_OFF)
        port.setParity(UsbSerialDevice.PARITY_NONE)
        port.setStopBits(UsbSerialDevice.STOP_BITS_1)

        maxInSize = getEpIn().maxPacketSize
        maxOutSize = getEpOut().maxPacketSize

        readDataBuf = ByteArray(maxInSize)

        log("in-size:$maxInSize out-size:$maxOutSize")
    }

    private fun getEpOut(): UsbEndpoint {
        val clz = port.javaClass
        val epIn = clz.getDeclaredField("outEndpoint")
        epIn.isAccessible = true
        return epIn.get(port) as UsbEndpoint
    }

    private fun getEpIn(): UsbEndpoint {
        val clz = port.javaClass
        val epIn = clz.getDeclaredField("inEndpoint")
        epIn.isAccessible = true
        return epIn.get(port) as UsbEndpoint
    }

    override fun close() {
    }

    override fun notifyClose() {
        port.syncClose()
    }

    override fun readBytes(buf: ByteArray): Int {
        val size = buf.size
        var len = size
        var index = 0
        while (true) {
            val n = readDataSize - readDataIndex
            if (n >= len) {
                System.arraycopy(readDataBuf, readDataIndex, buf, index, len)
                readDataIndex += len
                return size
            }
            if (n != 0) {
                System.arraycopy(readDataBuf, readDataIndex, buf, index, n)
                readDataIndex += n
                len -= n
                index += n
            }
            readData()
        }
    }

    override fun sync(): Int {
        var flag = false
        while (true) {
            val v = readByte()
            log("sync:$v")
            if (flag && (v == Proto.HEAD1)) {
                break
            }
            flag = v == Proto.HEAD0
        }
        return readByte()
    }

    override fun write(buf: ByteArray) {
        val size = buf.size
        var index = 0
        while (index < size) {
            var next = size - index
            if (next > maxOutSize) {
                next = maxOutSize
            }
            val n = port.syncWrite(buf, index, next, 5000)
            if (n <= 0) {
                throw IOException("发送失败")
            }
            index += n
        }
    }

    private fun readByte(): Int {
        while (true) {
            val n = readDataSize - readDataIndex
            if (n >= 1) {
                val value = readDataBuf[readDataIndex].toUint8()
                readDataIndex += 1
                return value
            }
            readData()
        }
    }

    private fun readData() {
        val n = port.syncRead(readDataBuf, 0)
        if (n < 0) {
            throw IOException("usb 读取错误 缓冲区异常或者已经断开")
        }
        readDataSize = n
        readDataIndex = 0
    }

}